package com.zx.idc.backend.gui.config.aspect;

import com.zx.idc.backend.gui.common.BaseApi;
import com.zx.idc.backend.gui.common.Result;
import com.zx.idc.backend.gui.common.ResultBuilder;
import com.zx.idc.common.exception.GlobalException;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletResponse;


/**
 * 处理 ajax 请求异常
 *
 * @author rxliuli
 */
@RestControllerAdvice
public class GlobalExceptionHandler extends BaseApi {
    /**
     * 404 异常（找不到处理）
     *
     * @param e 异常
     */
    @ExceptionHandler
    public Result<String> handlerException404(NoHandlerFoundException e, HttpServletResponse response) throws Exception {
        return handlerAjaxError(response, e, HttpStatus.NOT_FOUND, "请求接口路径错误");
    }

    /**
     * 400 异常（文件上传解析失败，基本上是客户端参数异常）
     *
     * @param e 异常
     */
    @ExceptionHandler
    public Result<String> handlerException400(MultipartException e, HttpServletResponse response) throws Exception {
        return handlerAjaxError(response, e, HttpStatus.BAD_REQUEST, "文件上传失败");
    }

    /**
     * 自定义的全局异常（手动抛出）
     *
     * @param e 异常
     */
    @ExceptionHandler
    public Result<String> handlerExceptionGlobal(GlobalException e, HttpServletResponse response) throws Exception {
        HttpStatus status = HttpStatus.valueOf(e.getCode() == null ? 500 : e.getCode());
        return handlerAjaxError(response, e, status, e.getMessage());
    }


    /**
     * 500 异常（内部错误）
     *
     * @param e 异常
     */
    @ExceptionHandler
    public Result<String> handlerException500(Exception e, HttpServletResponse response) throws Exception {
        if (e instanceof UnauthorizedException || e instanceof UnauthenticatedException) {
            throw e;
        }
//        return handlerAjaxError(response, e, HttpStatus.INTERNAL_SERVER_ERROR, "服务器内部发生了异常");
        return handlerAjaxError(response, e, HttpStatus.INTERNAL_SERVER_ERROR, "系统内部错误");
    }

    /**
     * 全局错误处理
     *
     * @param response http 响应
     * @param e        所捕获的异常类型
     * @param status   http 状态
     * @return 返回一个 ModelAndView（ajax 请求时返回 null）
     * @throws Exception 可能会不处理而直接抛出异常
     */
    private Result<String> handlerAjaxError(HttpServletResponse response, Exception e, HttpStatus status, String defaultMessage) throws Exception {
        //如果拦截的请求方法所在 Controller 存在 @ResponseStatus 注解时，则不应对其进行处理（越靠近真正执行方法的处理优先级就越高！）
        //@ResponseStatus 是使用在类上的注解，当类中存在未经处理的异常时就会返回指定的 HttpStatus 作为 HttpResponse
        if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) {
            throw e;
        }
        //此处应该记录错误日志
        log.error(e.getMessage(), e);
        //获取到错误消息（如果有的话）
        final String errorMessage = e instanceof GlobalException ? e.getMessage() : defaultMessage;
        //这两句防止中文乱码
        response.setContentType("text/html");
        response.setCharacterEncoding("UTF-8");
        response.setStatus(200);
        return ResultBuilder.error(status.value(), errorMessage);
    }
}
